#include <QTimer>

#include <Geom2d_Curve.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <GCPnts_AbscissaPoint.hxx>
#include <GCE2d_MakeSegment.hxx>
#include <Geom2dAPI_InterCurveCurve.hxx>
#include <Precision.hxx>
#include <GCPnts_UniformAbscissa.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <CPnts_AbscissaPoint.hxx>

#include "Qt3DPicker.h"
#include "OsgUtil.h"
#include "CombineCurveHandler.h"
#include "CmdManager.h"
#include "Scene.h"
#include "SendPointHandlerBase.h"
#include "SelectHandler3D.h"
#include "OSGNodeMask.h"
#include "Operation/OpAdd.h"
#include "LineStrip2D.h"
#include "LineStrip3D.h"
#include "Arc2D.h"
#include "Arc3D.h"
#include "Spline2D.h"
#include "Spline3D.h"
#include "QtOSGWidget.h"
#include "OcctShapeProp.h"

CombineCurveHandler::CombineCurveHandler()
{
}

CombineCurveHandler::~CombineCurveHandler()
{
}

void CombineCurveHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	auto scene = getScene();
	auto sph = getSendPointHandler();
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if (status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask);
			prompt(1, tr("选择平曲线: "));
		}
		else
		{
			emit finished(); 
		}
		return;
	}


	if (status() == 1)
	{
		if (selector && selector->done())
		{
			auto selections = selector->selectionSet();
			if (selections.size() > 0)
			{
				for (auto selection : selections)
				{
					CombineCurveData curveData;
					curveData.ent = std::dynamic_pointer_cast<Ent2DCurve>(selection);
					if (curveData.ent)
					{
						auto name = curveData.ent->metaObject()->className();
						auto parent = curveData.ent->getParent();
						UCS* ucs = dynamic_cast<UCS*>(parent);
						if (ucs && ucs->getKind() == UCSKind::XOY)
						{
							auto curves = curveData.ent->getCurve2d();
							auto C = curves[0];
							Geom2dAdaptor_Curve AC(C);
							curveData.lenth = GCPnts_AbscissaPoint::Length(AC, Precision::Confusion());
							SectionParam start, end;
							getStartEndParam(curves, start, end);
							gp_Pnt2d startPnt,endPnt;
							C->D0(start.param, startPnt);
							C->D0(end.param, endPnt);
							curveData.startPt = { double(startPnt.X()),double(startPnt.Y()),0 };
							curveData.endPt = { double(endPnt.X()),double(endPnt.Y()),0 };
							m_horizontalDatas.push_back(curveData);
						}
						else
						{
							remark(tr("请选择XOY平面上的曲线"));
							selector->clearSelection();
							emit finished();
							return;
						}
					}		
				}	
				if (sph)
				{
					sph->setPointMask(CS_LBTNDOWN | CS_MOUSEMOVE);
				}
				enableMouseHandler(MouseHandlerID::PointSender);
				prompt(2, tr("平曲线起点:"));
			}
		}
		return;
	}
	if (status() == 2)
	{
		if (data.type() == QVariant::Vector3D)
		{
			if (cs == CS_MOUSEMOVE)
				return;
			selector->clearSelection();
			auto pt = data.value<QVector3D>();
			osg::Vec3d lineStartPt = { pt.x(),pt.y(),pt.z() };
			bool success = sortDatas(m_horizontalDatas, lineStartPt);
			if (!success)
			{
				remark(tr("各段线必须首尾相接"));
				emit finished();
				return;
			}
			selector->beginSelect(D2LineMask | D2CurveMask);
			prompt(3, tr("选择竖曲线:"));  
		}
		return;
	}
	if (status() == 3)
	{
		if (selector && selector->done())
		{
			auto selections = selector->selectionSet();
			if (selections.size() > 0)
			{
				for (auto selection : selections)
				{
					CombineCurveData curveData;
					curveData.ent = std::dynamic_pointer_cast<Ent2DCurve>(selection);
					if (curveData.ent)
					{
						auto name = curveData.ent->metaObject()->className();
						auto parent = curveData.ent->getParent();
						UCS* ucs = dynamic_cast<UCS*>(parent);
						if (ucs && m_ucs == nullptr)
						{
							if (ucs->getKind() == UCSKind::YOZ || ucs->getKind() == UCSKind::ZOX)
							{
								m_ucs = ucs;
							}
							else
							{
								remark(tr("请选择YOZ或XOZ平面上的曲线"));
								selector->clearSelection();
								emit finished();
								return;
							}
						}
						if (ucs && ucs->getKind() == m_ucs->getKind())
						{
							auto curves = curveData.ent->getCurve2d();
							auto C = curves[0];
							SectionParam start, end;
							getStartEndParam(curves, start, end);
							gp_Pnt2d startPnt, endPnt;
							C->D0(start.param, startPnt);
							C->D0(end.param, endPnt);
							curveData.startPt = { double(startPnt.X()),double(startPnt.Y()),0 };
							curveData.endPt = { double(endPnt.X()),double(endPnt.Y()),0 };
							if (curveData.startPt.x() > curveData.endPt.x())
							{
								std::swap(curveData.startPt, curveData.endPt);
							}
							curveData.lenth = curveData.endPt.x() - curveData.startPt.x();
							m_verticalDatas.push_back(curveData);
						}
						else
						{
							remark(tr("竖曲线不在同一平面上"));
							selector->clearSelection();
							emit finished();
							return;
						}
					}
				}
				if (sph)
				{
					sph->setPointMask(CS_LBTNDOWN | CS_MOUSEMOVE);
				}
				enableMouseHandler(MouseHandlerID::PointSender);
				prompt(4, tr("竖曲线起点:"));
			}
		}
		return;
	}
	if (status() == 4)
	{
		if (data.type() == QVariant::Vector3D)
		{
			if (cs == CS_MOUSEMOVE)
				return;
			selector->clearSelection();
			auto pt = data.value<QVector3D>();
			osg::Vec3d lineStartPt;
			if(m_ucs->getKind() == UCSKind::ZOX)
				lineStartPt = { pt.x(),pt.z(),pt.y() };
			else
				lineStartPt = { pt.y(),pt.z(),pt.x() };
			bool success = sortDatas(m_verticalDatas, lineStartPt);
			if (!success)
			{
				remark(tr("各段线必须首尾相接"));
				emit finished();
				return;
			}
			double verticalStartX = lineStartPt.x();
			double hStartLen = 0;
			double vStartLen = 0;
			int vEntNum = 0;
			for (int i = 0; i < m_horizontalDatas.size(); i++)
			{
				auto horizontalData = m_horizontalDatas[i];
				for (vEntNum; vEntNum < m_verticalDatas.size(); vEntNum++)
				{
					auto verticalData = m_verticalDatas[vEntNum];
					projCompositeLine(horizontalData, verticalData, hStartLen, vStartLen);
					if (horizontalData.lenth - hStartLen < verticalData.lenth - vStartLen)
					{
						vStartLen += horizontalData.lenth - hStartLen;
						hStartLen = 0;
						break;
					}
					else
					{
						hStartLen += verticalData.lenth - vStartLen;
						vStartLen = 0;
					}
				} 
			}
			auto undoStack = scene->undoStack();
			undoStack->push(new OpAdd(m_ents));
			emit finished();
			return;
		}
		return;
	}
}

std::shared_ptr<Spline3D> makeFitCurve(INode* parent, const std::vector<osg::Vec3d>& points);

bool CombineCurveHandler::projCompositeLine(const CombineCurveData & horizontalData, const CombineCurveData & verticalData, const double hStartLen, const double vStartLen)
{
	std::vector<osg::Vec3d> points;
	auto hCurves = horizontalData.ent->getCurve2d();
	auto vCurves = verticalData.ent->getCurve2d();
	auto hCurve = hCurves[0];
	auto vCurve = vCurves[0];
	Geom2dAdaptor_Curve hC(hCurve);
	//竖曲线起始X坐标
	const double vStartX = verticalData.startPt.x() + vStartLen;
	//竖曲线终点X坐标
	const double vEndX = verticalData.endPt.x();
	double l1 = horizontalData.lenth - hStartLen;
	double l2 = vEndX - vStartX;
	double lenth = std::min(l1, l2);
	if (lenth == 0)
		return false;
	int n = lenth / 0.1;
	if (0.1 * n == lenth)
		n -= 1;
	double hStartParam, hEndParam;
	int size;
	if (horizontalData.reverse == true)
	{	
		CPnts_AbscissaPoint hPoint(hC, horizontalData.lenth - hStartLen, hCurve->FirstParameter(), -1);
		hStartParam = hPoint.Parameter();
		CPnts_AbscissaPoint hEndPoint(hC, horizontalData.lenth - hStartLen - 0.1 * n, hCurve->FirstParameter(), -1);
		hEndParam = hEndPoint.Parameter();
		size = n + 1;
	}
	else
	{
		CPnts_AbscissaPoint hPoint(hC, hStartLen, hCurve->FirstParameter(), -1);
		hStartParam = hPoint.Parameter();
		CPnts_AbscissaPoint hEndPoint(hC, hStartLen + lenth, hCurve->FirstParameter(), -1);
		hEndParam = hEndPoint.Parameter();
		size = n + 2;
	}
	GCPnts_UniformAbscissa uniAbs(hC, 0.1, hStartParam, hEndParam);
	if (!uniAbs.IsDone())
		return false;
	for (int i = 0; i < size; i++)
	{
		Standard_Real u;
		if(horizontalData.reverse == true)
			u = uniAbs.Parameter(size - i);
		else
			u = uniAbs.Parameter(i + 1);
		gp_Pnt2d hPnt;
		hCurve->D0(u, hPnt);
		double len = 0.1 * i;
		if (i == n + 1)
		{
			len = lenth;
		}
		GCE2d_MakeSegment maker(gp_Pnt2d(len + vStartX, 0), gp_Pnt2d(len + vStartX, 1));
		Handle(Geom2d_Curve) C = maker.Value();
		Geom2dAPI_InterCurveCurve inter;
		inter.Init(vCurve, basisCurve(C));
		if (inter.NbPoints() > 0)
		{
			//求出竖曲线上对应的点坐标
			auto interPt = inter.Point(1);
			points.push_back({ double(hPnt.X()),double(hPnt.Y()),double(interPt.Y()) });
		}
	}
	if (horizontalData.reverse == true)
	{
		CPnts_AbscissaPoint hPoint(hC, horizontalData.lenth - hStartLen - lenth, hCurve->FirstParameter(), -1);
		hEndParam = hPoint.Parameter();
		gp_Pnt2d hPnt;
		hCurve->D0(hEndParam, hPnt);
		GCE2d_MakeSegment maker(gp_Pnt2d(lenth + vStartX, 0), gp_Pnt2d(lenth + vStartX, 1));
		Handle(Geom2d_Curve) C = maker.Value();
		Geom2dAPI_InterCurveCurve inter;
		inter.Init(vCurve, basisCurve(C));
		if (inter.NbPoints() > 0)
		{
			//求出竖曲线上对应的点坐标
			auto interPt = inter.Point(1);
			points.push_back({ double(hPnt.X()),double(hPnt.Y()),double(interPt.Y()) });
		}
	}
	auto scene = getScene();
	if (horizontalData.ent->metaObject() == &LineStrip2D::staticMetaObject)
	{
		if (verticalData.ent->metaObject() == &LineStrip2D::staticMetaObject)
		{
			auto line = std::make_shared<LineStrip3D>(scene);
			std::vector<osg::Vec3d> pts;
			pts.push_back(points[0]);
			pts.push_back(points[points.size() - 1]);
			line->setPoints(pts);
			line->setColor(getCurrentColor());
			m_ents.insert(line);
		}
		else if (verticalData.ent->metaObject() == &Arc2D::staticMetaObject)
		{
			auto arc = std::make_shared<Arc3D>(scene);
			arc->setColor(getCurrentColor());
			if (points.size() > 2)
				arc->setCSE(points[0], points[1], points[points.size() - 1]);
			else
				return false;
			m_ents.insert(arc);
		}
		else if (verticalData.ent->metaObject() == &Spline2D::staticMetaObject)
		{
			std::shared_ptr<Spline3D> spline = makeFitCurve(scene, points);
			if (spline)
				spline->setColor(getCurrentColor());
			else
				return false;
			m_ents.insert(spline);
		}
	}
	else if (horizontalData.ent->metaObject() == &Arc2D::staticMetaObject ||
		horizontalData.ent->metaObject() == &Spline2D::staticMetaObject)
	{
		std::shared_ptr<Spline3D> spline = makeFitCurve(scene, points);
		if (spline)
			spline->setColor(getCurrentColor());
		else
			return false;
		m_ents.insert(spline);
	}
	else
	{
		return false;	
	}
	return true;
}

bool CombineCurveHandler::sortDatas(std::vector<CombineCurveData>& Datas, const osg::Vec3d & startPt)
{
	osg::Vec3d beforPt = startPt;
	//进行排序
	for (int index = 0; index < Datas.size(); index++)
	{
		for (int i = index; i < Datas.size(); i++)
		{
			const osg::Vec3d startPt = Datas[i].startPt;
			const osg::Vec3d endPt = Datas[i].endPt;
			if (pointApprox(startPt, beforPt))
			{
				beforPt = endPt;
				std::swap(Datas[index], Datas[i]);
				break;
			}
			else if (pointApprox(endPt, beforPt))
			{
				beforPt = startPt;
				Datas[i].reverse = true;
				std::swap(Datas[index], Datas[i]);
				break;
			}
			else
			{
				if (i == Datas.size() - 1)
				{
					return false;			
				}
			}
		}
	}	
	return true;
}


